1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26 package com.sun.imageio.plugins.png;
27
28 import java.awt.image.ColorModel;
29 import java.awt.image.IndexColorModel;
30 import java.awt.image.SampleModel;
31 import java.util.ArrayList;
32 import java.util.Iterator;
33 import java.util.StringTokenizer;
34 import javax.imageio.ImageTypeSpecifier;
35 import javax.imageio.metadata.IIOInvalidTreeException;
36 import javax.imageio.metadata.IIOMetadata;
37 import javax.imageio.metadata.IIOMetadataFormat;
38 import javax.imageio.metadata.IIOMetadataFormatImpl;
39 import javax.imageio.metadata.IIOMetadataNode;
40 import org.w3c.dom.Node;
41
42 public class PNGMetadata extends IIOMetadata implements Cloneable {
43
44
45 public static final String
46 nativeMetadataFormatName = "javax_imageio_png_1.0";
47
48 protected static final String nativeMetadataFormatClassName
49 = "com.sun.imageio.plugins.png.PNGMetadataFormat";
50
51
52 public static final String[] IHDR_colorTypeNames = {
53 "Grayscale", null, "RGB", "Palette",
54 "GrayAlpha", null, "RGBAlpha"
55 };
56
57 public static final int[] IHDR_numChannels = {
58 1, 0, 3, 3, 2, 0, 4
59 };
60
61
62 public static final String[] IHDR_bitDepths = {
63 "1", "2", "4", "8", "16"
64 };
65
66
67 public static final String[] IHDR_compressionMethodNames = {
68 "deflate"
69 };
70
71
72 public static final String[] IHDR_filterMethodNames = {
73 "adaptive"
74 };
75
76
77 public static final String[] IHDR_interlaceMethodNames = {
78 "none", "adam7"
79 };
80
81
82 public static final String[] iCCP_compressionMethodNames = {
83 "deflate"
84 };
85
86
87 public static final String[] zTXt_compressionMethodNames = {
88 "deflate"
89 };
90
91
92 public static final int PHYS_UNIT_UNKNOWN = 0;
93
94
95 public static final int PHYS_UNIT_METER = 1;
96
97
98 public static final String[] unitSpecifierNames = {
99 "unknown", "meter"
100 };
101
102
103 public static final String[] renderingIntentNames = {
104 "Perceptual",
105 "Relative colorimetric",
106 "Saturation",
107 "Absolute colorimetric"
108
109 };
110
111
112 public static final String[] colorSpaceTypeNames = {
113 "GRAY", null, "RGB", "RGB",
114 "GRAY", null, "RGB"
115 };
116
117
118 public boolean IHDR_present;
119 public int IHDR_width;
120 public int IHDR_height;
121 public int IHDR_bitDepth;
122 public int IHDR_colorType;
123 public int IHDR_compressionMethod;
124 public int IHDR_filterMethod;
125 public int IHDR_interlaceMethod;
126
127
128 public boolean PLTE_present;
129 public byte[] PLTE_red;
130 public byte[] PLTE_green;
131 public byte[] PLTE_blue;
132
133
134
135
136
137
138 public int[] PLTE_order = null;
139
140
141
142
143 public boolean bKGD_present;
144 public int bKGD_colorType;
145 public int bKGD_index;
146 public int bKGD_gray;
147 public int bKGD_red;
148 public int bKGD_green;
149 public int bKGD_blue;
150
151
152 public boolean cHRM_present;
153 public int cHRM_whitePointX;
154 public int cHRM_whitePointY;
155 public int cHRM_redX;
156 public int cHRM_redY;
157 public int cHRM_greenX;
158 public int cHRM_greenY;
159 public int cHRM_blueX;
160 public int cHRM_blueY;
161
162
163 public boolean gAMA_present;
164 public int gAMA_gamma;
165
166
167 public boolean hIST_present;
168 public char[] hIST_histogram;
169
170
171 public boolean iCCP_present;
172 public String iCCP_profileName;
173 public int iCCP_compressionMethod;
174 public byte[] iCCP_compressedProfile;
175
176
177 public ArrayList<String> iTXt_keyword = new ArrayList<String>();
178 public ArrayList<Boolean> iTXt_compressionFlag = new ArrayList<Boolean>();
179 public ArrayList<Integer> iTXt_compressionMethod = new ArrayList<Integer>();
180 public ArrayList<String> iTXt_languageTag = new ArrayList<String>();
181 public ArrayList<String> iTXt_translatedKeyword = new ArrayList<String>();
182 public ArrayList<String> iTXt_text = new ArrayList<String>();
183
184
185 public boolean pHYs_present;
186 public int pHYs_pixelsPerUnitXAxis;
187 public int pHYs_pixelsPerUnitYAxis;
188 public int pHYs_unitSpecifier;
189
190
191 public boolean sBIT_present;
192 public int sBIT_colorType;
193 public int sBIT_grayBits;
194 public int sBIT_redBits;
195 public int sBIT_greenBits;
196 public int sBIT_blueBits;
197 public int sBIT_alphaBits;
198
199
200 public boolean sPLT_present;
201 public String sPLT_paletteName;
202 public int sPLT_sampleDepth;
203 public int[] sPLT_red;
204 public int[] sPLT_green;
205 public int[] sPLT_blue;
206 public int[] sPLT_alpha;
207 public int[] sPLT_frequency;
208
209
210 public boolean sRGB_present;
211 public int sRGB_renderingIntent;
212
213
214 public ArrayList<String> tEXt_keyword = new ArrayList<String>();
215 public ArrayList<String> tEXt_text = new ArrayList<String>();
216
217
218 public boolean tIME_present;
219 public int tIME_year;
220 public int tIME_month;
221 public int tIME_day;
222 public int tIME_hour;
223 public int tIME_minute;
224 public int tIME_second;
225
226
227
228
229 public boolean tRNS_present;
230 public int tRNS_colorType;
231 public byte[] tRNS_alpha;
232 public int tRNS_gray;
233 public int tRNS_red;
234 public int tRNS_green;
235 public int tRNS_blue;
236
237
238 public ArrayList<String> zTXt_keyword = new ArrayList<String>();
239 public ArrayList<Integer> zTXt_compressionMethod = new ArrayList<Integer>();
240 public ArrayList<String> zTXt_text = new ArrayList<String>();
241
242
243 public ArrayList<String> unknownChunkType = new ArrayList<String>();
244 public ArrayList<byte[]> unknownChunkData = new ArrayList<byte[]>();
245
246 public PNGMetadata() {
247 super(true,
248 nativeMetadataFormatName,
249 nativeMetadataFormatClassName,
250 null, null);
251 }
252
253 public PNGMetadata(IIOMetadata metadata) {
254
255 }
256
257
258
259
260
261
262 public void initialize(ImageTypeSpecifier imageType, int numBands) {
263 ColorModel colorModel = imageType.getColorModel();
264 SampleModel sampleModel = imageType.getSampleModel();
265
266
267 int[] sampleSize = sampleModel.getSampleSize();
268 int bitDepth = sampleSize[0];
269
270
271 for (int i = 1; i < sampleSize.length; i++) {
272 if (sampleSize[i] > bitDepth) {
273 bitDepth = sampleSize[i];
274 }
275 }
276
277 if (sampleSize.length > 1 && bitDepth < 8) {
278 bitDepth = 8;
279 }
280
281
282 if (bitDepth > 2 && bitDepth < 4) {
283 bitDepth = 4;
284 } else if (bitDepth > 4 && bitDepth < 8) {
285 bitDepth = 8;
286 } else if (bitDepth > 8 && bitDepth < 16) {
287 bitDepth = 16;
288 } else if (bitDepth > 16) {
289 throw new RuntimeException("bitDepth > 16!");
290 }
291 IHDR_bitDepth = bitDepth;
292
293
294 if (colorModel instanceof IndexColorModel) {
295 IndexColorModel icm = (IndexColorModel)colorModel;
296 int size = icm.getMapSize();
297
298 byte[] reds = new byte[size];
299 icm.getReds(reds);
300 byte[] greens = new byte[size];
301 icm.getGreens(greens);
302 byte[] blues = new byte[size];
303 icm.getBlues(blues);
304
305
306
307 boolean isGray = false;
308 if (!IHDR_present ||
309 (IHDR_colorType != PNGImageReader.PNG_COLOR_PALETTE)) {
310 isGray = true;
311 int scale = 255/((1 << IHDR_bitDepth) - 1);
312 for (int i = 0; i < size; i++) {
313 byte red = reds[i];
314 if ((red != (byte)(i*scale)) ||
315 (red != greens[i]) ||
316 (red != blues[i])) {
317 isGray = false;
318 break;
319 }
320 }
321 }
322
323
324 boolean hasAlpha = colorModel.hasAlpha();
325
326 byte[] alpha = null;
327 if (hasAlpha) {
328 alpha = new byte[size];
329 icm.getAlphas(alpha);
330 }
331
332
333
334
335
336
337
338
339
340
341 if (isGray && hasAlpha && (bitDepth == 8 || bitDepth == 16)) {
342 IHDR_colorType = PNGImageReader.PNG_COLOR_GRAY_ALPHA;
343 } else if (isGray && !hasAlpha) {
344 IHDR_colorType = PNGImageReader.PNG_COLOR_GRAY;
345 } else {
346 IHDR_colorType = PNGImageReader.PNG_COLOR_PALETTE;
347 PLTE_present = true;
348 PLTE_order = null;
349 PLTE_red = (byte[])reds.clone();
350 PLTE_green = (byte[])greens.clone();
351 PLTE_blue = (byte[])blues.clone();
352
353 if (hasAlpha) {
354 tRNS_present = true;
355 tRNS_colorType = PNGImageReader.PNG_COLOR_PALETTE;
356
357 PLTE_order = new int[alpha.length];
358
359
360
361
362
363
364
365
366 byte[] newAlpha = new byte[alpha.length];
367
368
369
370 int newIndex = 0;
371 for (int i = 0; i < alpha.length; i++) {
372 if (alpha[i] != (byte)255) {
373 PLTE_order[i] = newIndex;
374 newAlpha[newIndex] = alpha[i];
375 ++newIndex;
376 }
377 }
378 int numTransparent = newIndex;
379
380
381
382 for (int i = 0; i < alpha.length; i++) {
383 if (alpha[i] == (byte)255) {
384 PLTE_order[i] = newIndex++;
385 }
386 }
387
388
389 byte[] oldRed = PLTE_red;
390 byte[] oldGreen = PLTE_green;
391 byte[] oldBlue = PLTE_blue;
392 int len = oldRed.length;
393 PLTE_red = new byte[len];
394 PLTE_green = new byte[len];
395 PLTE_blue = new byte[len];
396 for (int i = 0; i < len; i++) {
397 PLTE_red[PLTE_order[i]] = oldRed[i];
398 PLTE_green[PLTE_order[i]] = oldGreen[i];
399 PLTE_blue[PLTE_order[i]] = oldBlue[i];
400 }
401
402
403 tRNS_alpha = new byte[numTransparent];
404 System.arraycopy(newAlpha, 0,
405 tRNS_alpha, 0, numTransparent);
406 }
407 }
408 } else {
409 if (numBands == 1) {
410 IHDR_colorType = PNGImageReader.PNG_COLOR_GRAY;
411 } else if (numBands == 2) {
412 IHDR_colorType = PNGImageReader.PNG_COLOR_GRAY_ALPHA;
413 } else if (numBands == 3) {
414 IHDR_colorType = PNGImageReader.PNG_COLOR_RGB;
415 } else if (numBands == 4) {
416 IHDR_colorType = PNGImageReader.PNG_COLOR_RGB_ALPHA;
417 } else {
418 throw new RuntimeException("Number of bands not 1-4!");
419 }
420 }
421
422 IHDR_present = true;
423 }
424
425 public boolean isReadOnly() {
426 return false;
427 }
428
429 private ArrayList<byte[]> cloneBytesArrayList(ArrayList<byte[]> in) {
430 if (in == null) {
431 return null;
432 } else {
433 ArrayList<byte[]> list = new ArrayList<byte[]>(in.size());
434 for (byte[] b: in) {
435 list.add((b == null) ? null : (byte[])b.clone());
436 }
437 return list;
438 }
439 }
440
441
442 public Object clone() {
443 PNGMetadata metadata;
444 try {
445 metadata = (PNGMetadata)super.clone();
446 } catch (CloneNotSupportedException e) {
447 return null;
448 }
449
450
451 metadata.unknownChunkData =
452 cloneBytesArrayList(this.unknownChunkData);
453
454 return metadata;
455 }
456
457 public Node getAsTree(String formatName) {
458 if (formatName.equals(nativeMetadataFormatName)) {
459 return getNativeTree();
460 } else if (formatName.equals
461 (IIOMetadataFormatImpl.standardMetadataFormatName)) {
462 return getStandardTree();
463 } else {
464 throw new IllegalArgumentException("Not a recognized format!");
465 }
466 }
467
468 private Node getNativeTree() {
469 IIOMetadataNode node = null;
470 IIOMetadataNode root = new IIOMetadataNode(nativeMetadataFormatName);
471
472
473 if (IHDR_present) {
474 IIOMetadataNode IHDR_node = new IIOMetadataNode("IHDR");
475 IHDR_node.setAttribute("width", Integer.toString(IHDR_width));
476 IHDR_node.setAttribute("height", Integer.toString(IHDR_height));
477 IHDR_node.setAttribute("bitDepth",
478 Integer.toString(IHDR_bitDepth));
479 IHDR_node.setAttribute("colorType",
480 IHDR_colorTypeNames[IHDR_colorType]);
481
482 IHDR_node.setAttribute("compressionMethod",
483 IHDR_compressionMethodNames[IHDR_compressionMethod]);
484
485 IHDR_node.setAttribute("filterMethod",
486 IHDR_filterMethodNames[IHDR_filterMethod]);
487 IHDR_node.setAttribute("interlaceMethod",
488 IHDR_interlaceMethodNames[IHDR_interlaceMethod]);
489 root.appendChild(IHDR_node);
490 }
491
492
493 if (PLTE_present) {
494 IIOMetadataNode PLTE_node = new IIOMetadataNode("PLTE");
495 int numEntries = PLTE_red.length;
496 for (int i = 0; i < numEntries; i++) {
497 IIOMetadataNode entry = new IIOMetadataNode("PLTEEntry");
498 entry.setAttribute("index", Integer.toString(i));
499 entry.setAttribute("red",
500 Integer.toString(PLTE_red[i] & 0xff));
501 entry.setAttribute("green",
502 Integer.toString(PLTE_green[i] & 0xff));
503 entry.setAttribute("blue",
504 Integer.toString(PLTE_blue[i] & 0xff));
505 PLTE_node.appendChild(entry);
506 }
507
508 root.appendChild(PLTE_node);
509 }
510
511
512 if (bKGD_present) {
513 IIOMetadataNode bKGD_node = new IIOMetadataNode("bKGD");
514
515 if (bKGD_colorType == PNGImageReader.PNG_COLOR_PALETTE) {
516 node = new IIOMetadataNode("bKGD_Palette");
517 node.setAttribute("index", Integer.toString(bKGD_index));
518 } else if (bKGD_colorType == PNGImageReader.PNG_COLOR_GRAY) {
519 node = new IIOMetadataNode("bKGD_Grayscale");
520 node.setAttribute("gray", Integer.toString(bKGD_gray));
521 } else if (bKGD_colorType == PNGImageReader.PNG_COLOR_RGB) {
522 node = new IIOMetadataNode("bKGD_RGB");
523 node.setAttribute("red", Integer.toString(bKGD_red));
524 node.setAttribute("green", Integer.toString(bKGD_green));
525 node.setAttribute("blue", Integer.toString(bKGD_blue));
526 }
527 bKGD_node.appendChild(node);
528
529 root.appendChild(bKGD_node);
530 }
531
532
533 if (cHRM_present) {
534 IIOMetadataNode cHRM_node = new IIOMetadataNode("cHRM");
535 cHRM_node.setAttribute("whitePointX",
536 Integer.toString(cHRM_whitePointX));
537 cHRM_node.setAttribute("whitePointY",
538 Integer.toString(cHRM_whitePointY));
539 cHRM_node.setAttribute("redX", Integer.toString(cHRM_redX));
540 cHRM_node.setAttribute("redY", Integer.toString(cHRM_redY));
541 cHRM_node.setAttribute("greenX", Integer.toString(cHRM_greenX));
542 cHRM_node.setAttribute("greenY", Integer.toString(cHRM_greenY));
543 cHRM_node.setAttribute("blueX", Integer.toString(cHRM_blueX));
544 cHRM_node.setAttribute("blueY", Integer.toString(cHRM_blueY));
545
546 root.appendChild(cHRM_node);
547 }
548
549
550 if (gAMA_present) {
551 IIOMetadataNode gAMA_node = new IIOMetadataNode("gAMA");
552 gAMA_node.setAttribute("value", Integer.toString(gAMA_gamma));
553
554 root.appendChild(gAMA_node);
555 }
556
557
558 if (hIST_present) {
559 IIOMetadataNode hIST_node = new IIOMetadataNode("hIST");
560
561 for (int i = 0; i < hIST_histogram.length; i++) {
562 IIOMetadataNode hist =
563 new IIOMetadataNode("hISTEntry");
564 hist.setAttribute("index", Integer.toString(i));
565 hist.setAttribute("value",
566 Integer.toString(hIST_histogram[i]));
567 hIST_node.appendChild(hist);
568 }
569
570 root.appendChild(hIST_node);
571 }
572
573
574 if (iCCP_present) {
575 IIOMetadataNode iCCP_node = new IIOMetadataNode("iCCP");
576 iCCP_node.setAttribute("profileName", iCCP_profileName);
577 iCCP_node.setAttribute("compressionMethod",
578 iCCP_compressionMethodNames[iCCP_compressionMethod]);
579
580 Object profile = iCCP_compressedProfile;
581 if (profile != null) {
582 profile = ((byte[])profile).clone();
583 }
584 iCCP_node.setUserObject(profile);
585
586 root.appendChild(iCCP_node);
587 }
588
589
590 if (iTXt_keyword.size() > 0) {
591 IIOMetadataNode iTXt_parent = new IIOMetadataNode("iTXt");
592 for (int i = 0; i < iTXt_keyword.size(); i++) {
593 IIOMetadataNode iTXt_node = new IIOMetadataNode("iTXtEntry");
594 iTXt_node.setAttribute("keyword", iTXt_keyword.get(i));
595 iTXt_node.setAttribute("compressionFlag",
596 iTXt_compressionFlag.get(i) ? "TRUE" : "FALSE");
597 iTXt_node.setAttribute("compressionMethod",
598 iTXt_compressionMethod.get(i).toString());
599 iTXt_node.setAttribute("languageTag",
600 iTXt_languageTag.get(i));
601 iTXt_node.setAttribute("translatedKeyword",
602 iTXt_translatedKeyword.get(i));
603 iTXt_node.setAttribute("text", iTXt_text.get(i));
604
605 iTXt_parent.appendChild(iTXt_node);
606 }
607
608 root.appendChild(iTXt_parent);
609 }
610
611
612 if (pHYs_present) {
613 IIOMetadataNode pHYs_node = new IIOMetadataNode("pHYs");
614 pHYs_node.setAttribute("pixelsPerUnitXAxis",
615 Integer.toString(pHYs_pixelsPerUnitXAxis));
616 pHYs_node.setAttribute("pixelsPerUnitYAxis",
617 Integer.toString(pHYs_pixelsPerUnitYAxis));
618 pHYs_node.setAttribute("unitSpecifier",
619 unitSpecifierNames[pHYs_unitSpecifier]);
620
621 root.appendChild(pHYs_node);
622 }
623
624
625 if (sBIT_present) {
626 IIOMetadataNode sBIT_node = new IIOMetadataNode("sBIT");
627
628 if (sBIT_colorType == PNGImageReader.PNG_COLOR_GRAY) {
629 node = new IIOMetadataNode("sBIT_Grayscale");
630 node.setAttribute("gray",
631 Integer.toString(sBIT_grayBits));
632 } else if (sBIT_colorType == PNGImageReader.PNG_COLOR_GRAY_ALPHA) {
633 node = new IIOMetadataNode("sBIT_GrayAlpha");
634 node.setAttribute("gray",
635 Integer.toString(sBIT_grayBits));
636 node.setAttribute("alpha",
637 Integer.toString(sBIT_alphaBits));
638 } else if (sBIT_colorType == PNGImageReader.PNG_COLOR_RGB) {
639 node = new IIOMetadataNode("sBIT_RGB");
640 node.setAttribute("red",
641 Integer.toString(sBIT_redBits));
642 node.setAttribute("green",
643 Integer.toString(sBIT_greenBits));
644 node.setAttribute("blue",
645 Integer.toString(sBIT_blueBits));
646 } else if (sBIT_colorType == PNGImageReader.PNG_COLOR_RGB_ALPHA) {
647 node = new IIOMetadataNode("sBIT_RGBAlpha");
648 node.setAttribute("red",
649 Integer.toString(sBIT_redBits));
650 node.setAttribute("green",
651 Integer.toString(sBIT_greenBits));
652 node.setAttribute("blue",
653 Integer.toString(sBIT_blueBits));
654 node.setAttribute("alpha",
655 Integer.toString(sBIT_alphaBits));
656 } else if (sBIT_colorType == PNGImageReader.PNG_COLOR_PALETTE) {
657 node = new IIOMetadataNode("sBIT_Palette");
658 node.setAttribute("red",
659 Integer.toString(sBIT_redBits));
660 node.setAttribute("green",
661 Integer.toString(sBIT_greenBits));
662 node.setAttribute("blue",
663 Integer.toString(sBIT_blueBits));
664 }
665 sBIT_node.appendChild(node);
666
667 root.appendChild(sBIT_node);
668 }
669
670
671 if (sPLT_present) {
672 IIOMetadataNode sPLT_node = new IIOMetadataNode("sPLT");
673
674 sPLT_node.setAttribute("name", sPLT_paletteName);
675 sPLT_node.setAttribute("sampleDepth",
676 Integer.toString(sPLT_sampleDepth));
677
678 int numEntries = sPLT_red.length;
679 for (int i = 0; i < numEntries; i++) {
680 IIOMetadataNode entry = new IIOMetadataNode("sPLTEntry");
681 entry.setAttribute("index", Integer.toString(i));
682 entry.setAttribute("red", Integer.toString(sPLT_red[i]));
683 entry.setAttribute("green", Integer.toString(sPLT_green[i]));
684 entry.setAttribute("blue", Integer.toString(sPLT_blue[i]));
685 entry.setAttribute("alpha", Integer.toString(sPLT_alpha[i]));
686 entry.setAttribute("frequency",
687 Integer.toString(sPLT_frequency[i]));
688 sPLT_node.appendChild(entry);
689 }
690
691 root.appendChild(sPLT_node);
692 }
693
694
695 if (sRGB_present) {
696 IIOMetadataNode sRGB_node = new IIOMetadataNode("sRGB");
697 sRGB_node.setAttribute("renderingIntent",
698 renderingIntentNames[sRGB_renderingIntent]);
699
700 root.appendChild(sRGB_node);
701 }
702
703
704 if (tEXt_keyword.size() > 0) {
705 IIOMetadataNode tEXt_parent = new IIOMetadataNode("tEXt");
706 for (int i = 0; i < tEXt_keyword.size(); i++) {
707 IIOMetadataNode tEXt_node = new IIOMetadataNode("tEXtEntry");
708 tEXt_node.setAttribute("keyword" , (String)tEXt_keyword.get(i));
709 tEXt_node.setAttribute("value" , (String)tEXt_text.get(i));
710
711 tEXt_parent.appendChild(tEXt_node);
712 }
713
714 root.appendChild(tEXt_parent);
715 }
716
717
718 if (tIME_present) {
719 IIOMetadataNode tIME_node = new IIOMetadataNode("tIME");
720 tIME_node.setAttribute("year", Integer.toString(tIME_year));
721 tIME_node.setAttribute("month", Integer.toString(tIME_month));
722 tIME_node.setAttribute("day", Integer.toString(tIME_day));
723 tIME_node.setAttribute("hour", Integer.toString(tIME_hour));
724 tIME_node.setAttribute("minute", Integer.toString(tIME_minute));
725 tIME_node.setAttribute("second", Integer.toString(tIME_second));
726
727 root.appendChild(tIME_node);
728 }
729
730
731 if (tRNS_present) {
732 IIOMetadataNode tRNS_node = new IIOMetadataNode("tRNS");
733
734 if (tRNS_colorType == PNGImageReader.PNG_COLOR_PALETTE) {
735 node = new IIOMetadataNode("tRNS_Palette");
736
737 for (int i = 0; i < tRNS_alpha.length; i++) {
738 IIOMetadataNode entry =
739 new IIOMetadataNode("tRNS_PaletteEntry");
740 entry.setAttribute("index", Integer.toString(i));
741 entry.setAttribute("alpha",
742 Integer.toString(tRNS_alpha[i] & 0xff));
743 node.appendChild(entry);
744 }
745 } else if (tRNS_colorType == PNGImageReader.PNG_COLOR_GRAY) {
746 node = new IIOMetadataNode("tRNS_Grayscale");
747 node.setAttribute("gray", Integer.toString(tRNS_gray));
748 } else if (tRNS_colorType == PNGImageReader.PNG_COLOR_RGB) {
749 node = new IIOMetadataNode("tRNS_RGB");
750 node.setAttribute("red", Integer.toString(tRNS_red));
751 node.setAttribute("green", Integer.toString(tRNS_green));
752 node.setAttribute("blue", Integer.toString(tRNS_blue));
753 }
754 tRNS_node.appendChild(node);
755
756 root.appendChild(tRNS_node);
757 }
758
759
760 if (zTXt_keyword.size() > 0) {
761 IIOMetadataNode zTXt_parent = new IIOMetadataNode("zTXt");
762 for (int i = 0; i < zTXt_keyword.size(); i++) {
763 IIOMetadataNode zTXt_node = new IIOMetadataNode("zTXtEntry");
764 zTXt_node.setAttribute("keyword", (String)zTXt_keyword.get(i));
765
766 int cm = ((Integer)zTXt_compressionMethod.get(i)).intValue();
767 zTXt_node.setAttribute("compressionMethod",
768 zTXt_compressionMethodNames[cm]);
769
770 zTXt_node.setAttribute("text", (String)zTXt_text.get(i));
771
772 zTXt_parent.appendChild(zTXt_node);
773 }
774
775 root.appendChild(zTXt_parent);
776 }
777
778
779 if (unknownChunkType.size() > 0) {
780 IIOMetadataNode unknown_parent =
781 new IIOMetadataNode("UnknownChunks");
782 for (int i = 0; i < unknownChunkType.size(); i++) {
783 IIOMetadataNode unknown_node =
784 new IIOMetadataNode("UnknownChunk");
785 unknown_node.setAttribute("type",
786 (String)unknownChunkType.get(i));
787 unknown_node.setUserObject((byte[])unknownChunkData.get(i));
788
789 unknown_parent.appendChild(unknown_node);
790 }
791
792 root.appendChild(unknown_parent);
793 }
794
795 return root;
796 }
797
798 private int getNumChannels() {
799
800
801 int numChannels = IHDR_numChannels[IHDR_colorType];
802 if (IHDR_colorType == PNGImageReader.PNG_COLOR_PALETTE &&
803 tRNS_present && tRNS_colorType == IHDR_colorType) {
804 numChannels = 4;
805 }
806 return numChannels;
807 }
808
809 public IIOMetadataNode getStandardChromaNode() {
810 IIOMetadataNode chroma_node = new IIOMetadataNode("Chroma");
811 IIOMetadataNode node = null;
812
813 node = new IIOMetadataNode("ColorSpaceType");
814 node.setAttribute("name", colorSpaceTypeNames[IHDR_colorType]);
815 chroma_node.appendChild(node);
816
817 node = new IIOMetadataNode("NumChannels");
818 node.setAttribute("value", Integer.toString(getNumChannels()));
819 chroma_node.appendChild(node);
820
821 if (gAMA_present) {
822 node = new IIOMetadataNode("Gamma");
823 node.setAttribute("value", Float.toString(gAMA_gamma*1.0e-5F));
824 chroma_node.appendChild(node);
825 }
826
827 node = new IIOMetadataNode("BlackIsZero");
828 node.setAttribute("value", "TRUE");
829 chroma_node.appendChild(node);
830
831 if (PLTE_present) {
832 boolean hasAlpha = tRNS_present &&
833 (tRNS_colorType == PNGImageReader.PNG_COLOR_PALETTE);
834
835 node = new IIOMetadataNode("Palette");
836 for (int i = 0; i < PLTE_red.length; i++) {
837 IIOMetadataNode entry =
838 new IIOMetadataNode("PaletteEntry");
839 entry.setAttribute("index", Integer.toString(i));
840 entry.setAttribute("red",
841 Integer.toString(PLTE_red[i] & 0xff));
842 entry.setAttribute("green",
843 Integer.toString(PLTE_green[i] & 0xff));
844 entry.setAttribute("blue",
845 Integer.toString(PLTE_blue[i] & 0xff));
846 if (hasAlpha) {
847 int alpha = (i < tRNS_alpha.length) ?
848 (tRNS_alpha[i] & 0xff) : 255;
849 entry.setAttribute("alpha", Integer.toString(alpha));
850 }
851 node.appendChild(entry);
852 }
853 chroma_node.appendChild(node);
854 }
855
856 if (bKGD_present) {
857 if (bKGD_colorType == PNGImageReader.PNG_COLOR_PALETTE) {
858 node = new IIOMetadataNode("BackgroundIndex");
859 node.setAttribute("value", Integer.toString(bKGD_index));
860 } else {
861 node = new IIOMetadataNode("BackgroundColor");
862 int r, g, b;
863
864 if (bKGD_colorType == PNGImageReader.PNG_COLOR_GRAY) {
865 r = g = b = bKGD_gray;
866 } else {
867 r = bKGD_red;
868 g = bKGD_green;
869 b = bKGD_blue;
870 }
871 node.setAttribute("red", Integer.toString(r));
872 node.setAttribute("green", Integer.toString(g));
873 node.setAttribute("blue", Integer.toString(b));
874 }
875 chroma_node.appendChild(node);
876 }
877
878 return chroma_node;
879 }
880
881 public IIOMetadataNode getStandardCompressionNode() {
882 IIOMetadataNode compression_node = new IIOMetadataNode("Compression");
883 IIOMetadataNode node = null;
884
885 node = new IIOMetadataNode("CompressionTypeName");
886 node.setAttribute("value", "deflate");
887 compression_node.appendChild(node);
888
889 node = new IIOMetadataNode("Lossless");
890 node.setAttribute("value", "TRUE");
891 compression_node.appendChild(node);
892
893 node = new IIOMetadataNode("NumProgressiveScans");
894 node.setAttribute("value",
895 (IHDR_interlaceMethod == 0) ? "1" : "7");
896 compression_node.appendChild(node);
897
898 return compression_node;
899 }
900
901 private String repeat(String s, int times) {
902 if (times == 1) {
903 return s;
904 }
905 StringBuffer sb = new StringBuffer((s.length() + 1)*times - 1);
906 sb.append(s);
907 for (int i = 1; i < times; i++) {
908 sb.append(" ");
909 sb.append(s);
910 }
911 return sb.toString();
912 }
913
914 public IIOMetadataNode getStandardDataNode() {
915 IIOMetadataNode data_node = new IIOMetadataNode("Data");
916 IIOMetadataNode node = null;
917
918 node = new IIOMetadataNode("PlanarConfiguration");
919 node.setAttribute("value", "PixelInterleaved");
920 data_node.appendChild(node);
921
922 node = new IIOMetadataNode("SampleFormat");
923 node.setAttribute("value",
924 IHDR_colorType == PNGImageReader.PNG_COLOR_PALETTE ?
925 "Index" : "UnsignedIntegral");
926 data_node.appendChild(node);
927
928 String bitDepth = Integer.toString(IHDR_bitDepth);
929 node = new IIOMetadataNode("BitsPerSample");
930 node.setAttribute("value", repeat(bitDepth, getNumChannels()));
931 data_node.appendChild(node);
932
933 if (sBIT_present) {
934 node = new IIOMetadataNode("SignificantBitsPerSample");
935 String sbits;
936 if (sBIT_colorType == PNGImageReader.PNG_COLOR_GRAY ||
937 sBIT_colorType == PNGImageReader.PNG_COLOR_GRAY_ALPHA) {
938 sbits = Integer.toString(sBIT_grayBits);
939 } else {
940
941 sbits = Integer.toString(sBIT_redBits) + " " +
942 Integer.toString(sBIT_greenBits) + " " +
943 Integer.toString(sBIT_blueBits);
944 }
945
946 if (sBIT_colorType == PNGImageReader.PNG_COLOR_GRAY_ALPHA ||
947 sBIT_colorType == PNGImageReader.PNG_COLOR_RGB_ALPHA) {
948 sbits += " " + Integer.toString(sBIT_alphaBits);
949 }
950
951 node.setAttribute("value", sbits);
952 data_node.appendChild(node);
953 }
954
955
956
957 return data_node;
958 }
959
960 public IIOMetadataNode getStandardDimensionNode() {
961 IIOMetadataNode dimension_node = new IIOMetadataNode("Dimension");
962 IIOMetadataNode node = null;
963
964 node = new IIOMetadataNode("PixelAspectRatio");
965 float ratio = pHYs_present ?
966 (float)pHYs_pixelsPerUnitXAxis/pHYs_pixelsPerUnitYAxis : 1.0F;
967 node.setAttribute("value", Float.toString(ratio));
968 dimension_node.appendChild(node);
969
970 node = new IIOMetadataNode("ImageOrientation");
971 node.setAttribute("value", "Normal");
972 dimension_node.appendChild(node);
973
974 if (pHYs_present && pHYs_unitSpecifier == PHYS_UNIT_METER) {
975 node = new IIOMetadataNode("HorizontalPixelSize");
976 node.setAttribute("value",
977 Float.toString(1000.0F/pHYs_pixelsPerUnitXAxis));
978 dimension_node.appendChild(node);
979
980 node = new IIOMetadataNode("VerticalPixelSize");
981 node.setAttribute("value",
982 Float.toString(1000.0F/pHYs_pixelsPerUnitYAxis));
983 dimension_node.appendChild(node);
984 }
985
986 return dimension_node;
987 }
988
989 public IIOMetadataNode getStandardDocumentNode() {
990 if (!tIME_present) {
991 return null;
992 }
993
994 IIOMetadataNode document_node = new IIOMetadataNode("Document");
995 IIOMetadataNode node = null;
996
997 node = new IIOMetadataNode("ImageModificationTime");
998 node.setAttribute("year", Integer.toString(tIME_year));
999 node.setAttribute("month", Integer.toString(tIME_month));
1000 node.setAttribute("day", Integer.toString(tIME_day));
1001 node.setAttribute("hour", Integer.toString(tIME_hour));
1002 node.setAttribute("minute", Integer.toString(tIME_minute));
1003 node.setAttribute("second", Integer.toString(tIME_second));
1004 document_node.appendChild(node);
1005
1006 return document_node;
1007 }
1008
1009 public IIOMetadataNode getStandardTextNode() {
1010 int numEntries = tEXt_keyword.size() +
1011 iTXt_keyword.size() + zTXt_keyword.size();
1012 if (numEntries == 0) {
1013 return null;
1014 }
1015
1016 IIOMetadataNode text_node = new IIOMetadataNode("Text");
1017 IIOMetadataNode node = null;
1018
1019 for (int i = 0; i < tEXt_keyword.size(); i++) {
1020 node = new IIOMetadataNode("TextEntry");
1021 node.setAttribute("keyword", (String)tEXt_keyword.get(i));
1022 node.setAttribute("value", (String)tEXt_text.get(i));
1023 node.setAttribute("encoding", "ISO-8859-1");
1024 node.setAttribute("compression", "none");
1025
1026 text_node.appendChild(node);
1027 }
1028
1029 for (int i = 0; i < iTXt_keyword.size(); i++) {
1030 node = new IIOMetadataNode("TextEntry");
1031 node.setAttribute("keyword", iTXt_keyword.get(i));
1032 node.setAttribute("value", iTXt_text.get(i));
1033 node.setAttribute("language",
1034 iTXt_languageTag.get(i));
1035 if (iTXt_compressionFlag.get(i)) {
1036 node.setAttribute("compression", "zip");
1037 } else {
1038 node.setAttribute("compression", "none");
1039 }
1040
1041 text_node.appendChild(node);
1042 }
1043
1044 for (int i = 0; i < zTXt_keyword.size(); i++) {
1045 node = new IIOMetadataNode("TextEntry");
1046 node.setAttribute("keyword", (String)zTXt_keyword.get(i));
1047 node.setAttribute("value", (String)zTXt_text.get(i));
1048 node.setAttribute("compression", "zip");
1049
1050 text_node.appendChild(node);
1051 }
1052
1053 return text_node;
1054 }
1055
1056 public IIOMetadataNode getStandardTransparencyNode() {
1057 IIOMetadataNode transparency_node =
1058 new IIOMetadataNode("Transparency");
1059 IIOMetadataNode node = null;
1060
1061 node = new IIOMetadataNode("Alpha");
1062 boolean hasAlpha =
1063 (IHDR_colorType == PNGImageReader.PNG_COLOR_RGB_ALPHA) ||
1064 (IHDR_colorType == PNGImageReader.PNG_COLOR_GRAY_ALPHA) ||
1065 (IHDR_colorType == PNGImageReader.PNG_COLOR_PALETTE &&
1066 tRNS_present &&
1067 (tRNS_colorType == IHDR_colorType) &&
1068 (tRNS_alpha != null));
1069 node.setAttribute("value", hasAlpha ? "nonpremultipled" : "none");
1070 transparency_node.appendChild(node);
1071
1072 if (tRNS_present) {
1073 node = new IIOMetadataNode("TransparentColor");
1074 if (tRNS_colorType == PNGImageReader.PNG_COLOR_RGB) {
1075 node.setAttribute("value",
1076 Integer.toString(tRNS_red) + " " +
1077 Integer.toString(tRNS_green) + " " +
1078 Integer.toString(tRNS_blue));
1079 } else if (tRNS_colorType == PNGImageReader.PNG_COLOR_GRAY) {
1080 node.setAttribute("value", Integer.toString(tRNS_gray));
1081 }
1082 transparency_node.appendChild(node);
1083 }
1084
1085 return transparency_node;
1086 }
1087
1088
1089 private void fatal(Node node, String reason)
1090 throws IIOInvalidTreeException {
1091 throw new IIOInvalidTreeException(reason, node);
1092 }
1093
1094
1095 private String getStringAttribute(Node node, String name,
1096 String defaultValue, boolean required)
1097 throws IIOInvalidTreeException {
1098 Node attr = node.getAttributes().getNamedItem(name);
1099 if (attr == null) {
1100 if (!required) {
1101 return defaultValue;
1102 } else {
1103 fatal(node, "Required attribute " + name + " not present!");
1104 }
1105 }
1106 return attr.getNodeValue();
1107 }
1108
1109
1110
1111 private int getIntAttribute(Node node, String name,
1112 int defaultValue, boolean required)
1113 throws IIOInvalidTreeException {
1114 String value = getStringAttribute(node, name, null, required);
1115 if (value == null) {
1116 return defaultValue;
1117 }
1118 return Integer.parseInt(value);
1119 }
1120
1121
1122 private float getFloatAttribute(Node node, String name,
1123 float defaultValue, boolean required)
1124 throws IIOInvalidTreeException {
1125 String value = getStringAttribute(node, name, null, required);
1126 if (value == null) {
1127 return defaultValue;
1128 }
1129 return Float.parseFloat(value);
1130 }
1131
1132
1133 private int getIntAttribute(Node node, String name)
1134 throws IIOInvalidTreeException {
1135 return getIntAttribute(node, name, -1, true);
1136 }
1137
1138
1139 private float getFloatAttribute(Node node, String name)
1140 throws IIOInvalidTreeException {
1141 return getFloatAttribute(node, name, -1.0F, true);
1142 }
1143
1144
1145 private boolean getBooleanAttribute(Node node, String name,
1146 boolean defaultValue,
1147 boolean required)
1148 throws IIOInvalidTreeException {
1149 Node attr = node.getAttributes().getNamedItem(name);
1150 if (attr == null) {
1151 if (!required) {
1152 return defaultValue;
1153 } else {
1154 fatal(node, "Required attribute " + name + " not present!");
1155 }
1156 }
1157 String value = attr.getNodeValue();
1158
1159 if (value.equals("TRUE") || value.equals("true")) {
1160 return true;
1161 } else if (value.equals("FALSE") || value.equals("false")) {
1162 return false;
1163 } else {
1164 fatal(node, "Attribute " + name + " must be 'TRUE' or 'FALSE'!");
1165 return false;
1166 }
1167 }
1168
1169
1170 private boolean getBooleanAttribute(Node node, String name)
1171 throws IIOInvalidTreeException {
1172 return getBooleanAttribute(node, name, false, true);
1173 }
1174
1175
1176 private int getEnumeratedAttribute(Node node,
1177 String name, String[] legalNames,
1178 int defaultValue, boolean required)
1179 throws IIOInvalidTreeException {
1180 Node attr = node.getAttributes().getNamedItem(name);
1181 if (attr == null) {
1182 if (!required) {
1183 return defaultValue;
1184 } else {
1185 fatal(node, "Required attribute " + name + " not present!");
1186 }
1187 }
1188 String value = attr.getNodeValue();
1189 for (int i = 0; i < legalNames.length; i++) {
1190 if (value.equals(legalNames[i])) {
1191 return i;
1192 }
1193 }
1194
1195 fatal(node, "Illegal value for attribute " + name + "!");
1196 return -1;
1197 }
1198
1199
1200 private int getEnumeratedAttribute(Node node,
1201 String name, String[] legalNames)
1202 throws IIOInvalidTreeException {
1203 return getEnumeratedAttribute(node, name, legalNames, -1, true);
1204 }
1205
1206
1207 private String getAttribute(Node node, String name,
1208 String defaultValue, boolean required)
1209 throws IIOInvalidTreeException {
1210 Node attr = node.getAttributes().getNamedItem(name);
1211 if (attr == null) {
1212 if (!required) {
1213 return defaultValue;
1214 } else {
1215 fatal(node, "Required attribute " + name + " not present!");
1216 }
1217 }
1218 return attr.getNodeValue();
1219 }
1220
1221
1222 private String getAttribute(Node node, String name)
1223 throws IIOInvalidTreeException {
1224 return getAttribute(node, name, null, true);
1225 }
1226
1227 public void mergeTree(String formatName, Node root)
1228 throws IIOInvalidTreeException {
1229 if (formatName.equals(nativeMetadataFormatName)) {
1230 if (root == null) {
1231 throw new IllegalArgumentException("root == null!");
1232 }
1233 mergeNativeTree(root);
1234 } else if (formatName.equals
1235 (IIOMetadataFormatImpl.standardMetadataFormatName)) {
1236 if (root == null) {
1237 throw new IllegalArgumentException("root == null!");
1238 }
1239 mergeStandardTree(root);
1240 } else {
1241 throw new IllegalArgumentException("Not a recognized format!");
1242 }
1243 }
1244
1245 private void mergeNativeTree(Node root)
1246 throws IIOInvalidTreeException {
1247 Node node = root;
1248 if (!node.getNodeName().equals(nativeMetadataFormatName)) {
1249 fatal(node, "Root must be " + nativeMetadataFormatName);
1250 }
1251
1252 node = node.getFirstChild();
1253 while (node != null) {
1254 String name = node.getNodeName();
1255
1256 if (name.equals("IHDR")) {
1257 IHDR_width = getIntAttribute(node, "width");
1258 IHDR_height = getIntAttribute(node, "height");
1259 IHDR_bitDepth = getEnumeratedAttribute(node, "bitDepth",
1260 IHDR_bitDepths);
1261 IHDR_colorType = getEnumeratedAttribute(node, "colorType",
1262 IHDR_colorTypeNames);
1263 IHDR_compressionMethod =
1264 getEnumeratedAttribute(node, "compressionMethod",
1265 IHDR_compressionMethodNames);
1266 IHDR_filterMethod =
1267 getEnumeratedAttribute(node,
1268 "filterMethod",
1269 IHDR_filterMethodNames);
1270 IHDR_interlaceMethod =
1271 getEnumeratedAttribute(node, "interlaceMethod",
1272 IHDR_interlaceMethodNames);
1273 IHDR_present = true;
1274 } else if (name.equals("PLTE")) {
1275 byte[] red = new byte[256];
1276 byte[] green = new byte[256];
1277 byte[] blue = new byte[256];
1278 int maxindex = -1;
1279
1280 Node PLTE_entry = node.getFirstChild();
1281 if (PLTE_entry == null) {
1282 fatal(node, "Palette has no entries!");
1283 }
1284
1285 while (PLTE_entry != null) {
1286 if (!PLTE_entry.getNodeName().equals("PLTEEntry")) {
1287 fatal(node,
1288 "Only a PLTEEntry may be a child of a PLTE!");
1289 }
1290
1291 int index = getIntAttribute(PLTE_entry, "index");
1292 if (index < 0 || index > 255) {
1293 fatal(node,
1294 "Bad value for PLTEEntry attribute index!");
1295 }
1296 if (index > maxindex) {
1297 maxindex = index;
1298 }
1299 red[index] =
1300 (byte)getIntAttribute(PLTE_entry, "red");
1301 green[index] =
1302 (byte)getIntAttribute(PLTE_entry, "green");
1303 blue[index] =
1304 (byte)getIntAttribute(PLTE_entry, "blue");
1305
1306 PLTE_entry = PLTE_entry.getNextSibling();
1307 }
1308
1309 int numEntries = maxindex + 1;
1310 PLTE_red = new byte[numEntries];
1311 PLTE_green = new byte[numEntries];
1312 PLTE_blue = new byte[numEntries];
1313 System.arraycopy(red, 0, PLTE_red, 0, numEntries);
1314 System.arraycopy(green, 0, PLTE_green, 0, numEntries);
1315 System.arraycopy(blue, 0, PLTE_blue, 0, numEntries);
1316 PLTE_present = true;
1317 } else if (name.equals("bKGD")) {
1318 bKGD_present = false;
1319 Node bKGD_node = node.getFirstChild();
1320 if (bKGD_node == null) {
1321 fatal(node, "bKGD node has no children!");
1322 }
1323 String bKGD_name = bKGD_node.getNodeName();
1324 if (bKGD_name.equals("bKGD_Palette")) {
1325 bKGD_index = getIntAttribute(bKGD_node, "index");
1326 bKGD_colorType = PNGImageReader.PNG_COLOR_PALETTE;
1327 } else if (bKGD_name.equals("bKGD_Grayscale")) {
1328 bKGD_gray = getIntAttribute(bKGD_node, "gray");
1329 bKGD_colorType = PNGImageReader.PNG_COLOR_GRAY;
1330 } else if (bKGD_name.equals("bKGD_RGB")) {
1331 bKGD_red = getIntAttribute(bKGD_node, "red");
1332 bKGD_green = getIntAttribute(bKGD_node, "green");
1333 bKGD_blue = getIntAttribute(bKGD_node, "blue");
1334 bKGD_colorType = PNGImageReader.PNG_COLOR_RGB;
1335 } else {
1336 fatal(node, "Bad child of a bKGD node!");
1337 }
1338 if (bKGD_node.getNextSibling() != null) {
1339 fatal(node, "bKGD node has more than one child!");
1340 }
1341
1342 bKGD_present = true;
1343 } else if (name.equals("cHRM")) {
1344 cHRM_whitePointX = getIntAttribute(node, "whitePointX");
1345 cHRM_whitePointY = getIntAttribute(node, "whitePointY");
1346 cHRM_redX = getIntAttribute(node, "redX");
1347 cHRM_redY = getIntAttribute(node, "redY");
1348 cHRM_greenX = getIntAttribute(node, "greenX");
1349 cHRM_greenY = getIntAttribute(node, "greenY");
1350 cHRM_blueX = getIntAttribute(node, "blueX");
1351 cHRM_blueY = getIntAttribute(node, "blueY");
1352
1353 cHRM_present = true;
1354 } else if (name.equals("gAMA")) {
1355 gAMA_gamma = getIntAttribute(node, "value");
1356 gAMA_present = true;
1357 } else if (name.equals("hIST")) {
1358 char[] hist = new char[256];
1359 int maxindex = -1;
1360
1361 Node hIST_entry = node.getFirstChild();
1362 if (hIST_entry == null) {
1363 fatal(node, "hIST node has no children!");
1364 }
1365
1366 while (hIST_entry != null) {
1367 if (!hIST_entry.getNodeName().equals("hISTEntry")) {
1368 fatal(node,
1369 "Only a hISTEntry may be a child of a hIST!");
1370 }
1371
1372 int index = getIntAttribute(hIST_entry, "index");
1373 if (index < 0 || index > 255) {
1374 fatal(node,
1375 "Bad value for histEntry attribute index!");
1376 }
1377 if (index > maxindex) {
1378 maxindex = index;
1379 }
1380 hist[index] =
1381 (char)getIntAttribute(hIST_entry, "value");
1382
1383 hIST_entry = hIST_entry.getNextSibling();
1384 }
1385
1386 int numEntries = maxindex + 1;
1387 hIST_histogram = new char[numEntries];
1388 System.arraycopy(hist, 0, hIST_histogram, 0, numEntries);
1389
1390 hIST_present = true;
1391 } else if (name.equals("iCCP")) {
1392 iCCP_profileName = getAttribute(node, "profileName");
1393 iCCP_compressionMethod =
1394 getEnumeratedAttribute(node, "compressionMethod",
1395 iCCP_compressionMethodNames);
1396 Object compressedProfile =
1397 ((IIOMetadataNode)node).getUserObject();
1398 if (compressedProfile == null) {
1399 fatal(node, "No ICCP profile present in user object!");
1400 }
1401 if (!(compressedProfile instanceof byte[])) {
1402 fatal(node, "User object not a byte array!");
1403 }
1404
1405 iCCP_compressedProfile =
1406 (byte[])((byte[])compressedProfile).clone();
1407
1408 iCCP_present = true;
1409 } else if (name.equals("iTXt")) {
1410 Node iTXt_node = node.getFirstChild();
1411 while (iTXt_node != null) {
1412 if (!iTXt_node.getNodeName().equals("iTXtEntry")) {
1413 fatal(node,
1414 "Only an iTXtEntry may be a child of an iTXt!");
1415 }
1416
1417 String keyword = getAttribute(iTXt_node, "keyword");
1418 if (isValidKeyword(keyword)) {
1419 iTXt_keyword.add(keyword);
1420
1421 boolean compressionFlag =
1422 getBooleanAttribute(iTXt_node, "compressionFlag");
1423 iTXt_compressionFlag.add(Boolean.valueOf(compressionFlag));
1424
1425 String compressionMethod =
1426 getAttribute(iTXt_node, "compressionMethod");
1427 iTXt_compressionMethod.add(Integer.valueOf(compressionMethod));
1428
1429 String languageTag =
1430 getAttribute(iTXt_node, "languageTag");
1431 iTXt_languageTag.add(languageTag);
1432
1433 String translatedKeyword =
1434 getAttribute(iTXt_node, "translatedKeyword");
1435 iTXt_translatedKeyword.add(translatedKeyword);
1436
1437 String text = getAttribute(iTXt_node, "text");
1438 iTXt_text.add(text);
1439
1440 }
1441
1442
1443 iTXt_node = iTXt_node.getNextSibling();
1444 }
1445 } else if (name.equals("pHYs")) {
1446 pHYs_pixelsPerUnitXAxis =
1447 getIntAttribute(node, "pixelsPerUnitXAxis");
1448 pHYs_pixelsPerUnitYAxis =
1449 getIntAttribute(node, "pixelsPerUnitYAxis");
1450 pHYs_unitSpecifier =
1451 getEnumeratedAttribute(node, "unitSpecifier",
1452 unitSpecifierNames);
1453
1454 pHYs_present = true;
1455 } else if (name.equals("sBIT")) {
1456 sBIT_present = false;
1457 Node sBIT_node = node.getFirstChild();
1458 if (sBIT_node == null) {
1459 fatal(node, "sBIT node has no children!");
1460 }
1461 String sBIT_name = sBIT_node.getNodeName();
1462 if (sBIT_name.equals("sBIT_Grayscale")) {
1463 sBIT_grayBits = getIntAttribute(sBIT_node, "gray");
1464 sBIT_colorType = PNGImageReader.PNG_COLOR_GRAY;
1465 } else if (sBIT_name.equals("sBIT_GrayAlpha")) {
1466 sBIT_grayBits = getIntAttribute(sBIT_node, "gray");
1467 sBIT_alphaBits = getIntAttribute(sBIT_node, "alpha");
1468 sBIT_colorType = PNGImageReader.PNG_COLOR_GRAY_ALPHA;
1469 } else if (sBIT_name.equals("sBIT_RGB")) {
1470 sBIT_redBits = getIntAttribute(sBIT_node, "red");
1471 sBIT_greenBits = getIntAttribute(sBIT_node, "green");
1472 sBIT_blueBits = getIntAttribute(sBIT_node, "blue");
1473 sBIT_colorType = PNGImageReader.PNG_COLOR_RGB;
1474 } else if (sBIT_name.equals("sBIT_RGBAlpha")) {
1475 sBIT_redBits = getIntAttribute(sBIT_node, "red");
1476 sBIT_greenBits = getIntAttribute(sBIT_node, "green");
1477 sBIT_blueBits = getIntAttribute(sBIT_node, "blue");
1478 sBIT_alphaBits = getIntAttribute(sBIT_node, "alpha");
1479 sBIT_colorType = PNGImageReader.PNG_COLOR_RGB_ALPHA;
1480 } else if (sBIT_name.equals("sBIT_Palette")) {
1481 sBIT_redBits = getIntAttribute(sBIT_node, "red");
1482 sBIT_greenBits = getIntAttribute(sBIT_node, "green");
1483 sBIT_blueBits = getIntAttribute(sBIT_node, "blue");
1484 sBIT_colorType = PNGImageReader.PNG_COLOR_PALETTE;
1485 } else {
1486 fatal(node, "Bad child of an sBIT node!");
1487 }
1488 if (sBIT_node.getNextSibling() != null) {
1489 fatal(node, "sBIT node has more than one child!");
1490 }
1491
1492 sBIT_present = true;
1493 } else if (name.equals("sPLT")) {
1494 sPLT_paletteName = getAttribute(node, "name");
1495 sPLT_sampleDepth = getIntAttribute(node, "sampleDepth");
1496
1497 int[] red = new int[256];
1498 int[] green = new int[256];
1499 int[] blue = new int[256];
1500 int[] alpha = new int[256];
1501 int[] frequency = new int[256];
1502 int maxindex = -1;
1503
1504 Node sPLT_entry = node.getFirstChild();
1505 if (sPLT_entry == null) {
1506 fatal(node, "sPLT node has no children!");
1507 }
1508
1509 while (sPLT_entry != null) {
1510 if (!sPLT_entry.getNodeName().equals("sPLTEntry")) {
1511 fatal(node,
1512 "Only an sPLTEntry may be a child of an sPLT!");
1513 }
1514
1515 int index = getIntAttribute(sPLT_entry, "index");
1516 if (index < 0 || index > 255) {
1517 fatal(node,
1518 "Bad value for PLTEEntry attribute index!");
1519 }
1520 if (index > maxindex) {
1521 maxindex = index;
1522 }
1523 red[index] = getIntAttribute(sPLT_entry, "red");
1524 green[index] = getIntAttribute(sPLT_entry, "green");
1525 blue[index] = getIntAttribute(sPLT_entry, "blue");
1526 alpha[index] = getIntAttribute(sPLT_entry, "alpha");
1527 frequency[index] =
1528 getIntAttribute(sPLT_entry, "frequency");
1529
1530 sPLT_entry = sPLT_entry.getNextSibling();
1531 }
1532
1533 int numEntries = maxindex + 1;
1534 sPLT_red = new int[numEntries];
1535 sPLT_green = new int[numEntries];
1536 sPLT_blue = new int[numEntries];
1537 sPLT_alpha = new int[numEntries];
1538 sPLT_frequency = new int[numEntries];
1539 System.arraycopy(red, 0, sPLT_red, 0, numEntries);
1540 System.arraycopy(green, 0, sPLT_green, 0, numEntries);
1541 System.arraycopy(blue, 0, sPLT_blue, 0, numEntries);
1542 System.arraycopy(alpha, 0, sPLT_alpha, 0, numEntries);
1543 System.arraycopy(frequency, 0,
1544 sPLT_frequency, 0, numEntries);
1545
1546 sPLT_present = true;
1547 } else if (name.equals("sRGB")) {
1548 sRGB_renderingIntent =
1549 getEnumeratedAttribute(node, "renderingIntent",
1550 renderingIntentNames);
1551
1552 sRGB_present = true;
1553 } else if (name.equals("tEXt")) {
1554 Node tEXt_node = node.getFirstChild();
1555 while (tEXt_node != null) {
1556 if (!tEXt_node.getNodeName().equals("tEXtEntry")) {
1557 fatal(node,
1558 "Only an tEXtEntry may be a child of an tEXt!");
1559 }
1560
1561 String keyword = getAttribute(tEXt_node, "keyword");
1562 tEXt_keyword.add(keyword);
1563
1564 String text = getAttribute(tEXt_node, "value");
1565 tEXt_text.add(text);
1566
1567 tEXt_node = tEXt_node.getNextSibling();
1568 }
1569 } else if (name.equals("tIME")) {
1570 tIME_year = getIntAttribute(node, "year");
1571 tIME_month = getIntAttribute(node, "month");
1572 tIME_day = getIntAttribute(node, "day");
1573 tIME_hour = getIntAttribute(node, "hour");
1574 tIME_minute = getIntAttribute(node, "minute");
1575 tIME_second = getIntAttribute(node, "second");
1576
1577 tIME_present = true;
1578 } else if (name.equals("tRNS")) {
1579 tRNS_present = false;
1580 Node tRNS_node = node.getFirstChild();
1581 if (tRNS_node == null) {
1582 fatal(node, "tRNS node has no children!");
1583 }
1584 String tRNS_name = tRNS_node.getNodeName();
1585 if (tRNS_name.equals("tRNS_Palette")) {
1586 byte[] alpha = new byte[256];
1587 int maxindex = -1;
1588
1589 Node tRNS_paletteEntry = tRNS_node.getFirstChild();
1590 if (tRNS_paletteEntry == null) {
1591 fatal(node, "tRNS_Palette node has no children!");
1592 }
1593 while (tRNS_paletteEntry != null) {
1594 if (!tRNS_paletteEntry.getNodeName().equals(
1595 "tRNS_PaletteEntry")) {
1596 fatal(node,
1597 "Only a tRNS_PaletteEntry may be a child of a tRNS_Palette!");
1598 }
1599 int index =
1600 getIntAttribute(tRNS_paletteEntry, "index");
1601 if (index < 0 || index > 255) {
1602 fatal(node,
1603 "Bad value for tRNS_PaletteEntry attribute index!");
1604 }
1605 if (index > maxindex) {
1606 maxindex = index;
1607 }
1608 alpha[index] =
1609 (byte)getIntAttribute(tRNS_paletteEntry,
1610 "alpha");
1611
1612 tRNS_paletteEntry =
1613 tRNS_paletteEntry.getNextSibling();
1614 }
1615
1616 int numEntries = maxindex + 1;
1617 tRNS_alpha = new byte[numEntries];
1618 tRNS_colorType = PNGImageReader.PNG_COLOR_PALETTE;
1619 System.arraycopy(alpha, 0, tRNS_alpha, 0, numEntries);
1620 } else if (tRNS_name.equals("tRNS_Grayscale")) {
1621 tRNS_gray = getIntAttribute(tRNS_node, "gray");
1622 tRNS_colorType = PNGImageReader.PNG_COLOR_GRAY;
1623 } else if (tRNS_name.equals("tRNS_RGB")) {
1624 tRNS_red = getIntAttribute(tRNS_node, "red");
1625 tRNS_green = getIntAttribute(tRNS_node, "green");
1626 tRNS_blue = getIntAttribute(tRNS_node, "blue");
1627 tRNS_colorType = PNGImageReader.PNG_COLOR_RGB;
1628 } else {
1629 fatal(node, "Bad child of a tRNS node!");
1630 }
1631 if (tRNS_node.getNextSibling() != null) {
1632 fatal(node, "tRNS node has more than one child!");
1633 }
1634
1635 tRNS_present = true;
1636 } else if (name.equals("zTXt")) {
1637 Node zTXt_node = node.getFirstChild();
1638 while (zTXt_node != null) {
1639 if (!zTXt_node.getNodeName().equals("zTXtEntry")) {
1640 fatal(node,
1641 "Only an zTXtEntry may be a child of an zTXt!");
1642 }
1643
1644 String keyword = getAttribute(zTXt_node, "keyword");
1645 zTXt_keyword.add(keyword);
1646
1647 int compressionMethod =
1648 getEnumeratedAttribute(zTXt_node, "compressionMethod",
1649 zTXt_compressionMethodNames);
1650 zTXt_compressionMethod.add(new Integer(compressionMethod));
1651
1652 String text = getAttribute(zTXt_node, "text");
1653 zTXt_text.add(text);
1654
1655 zTXt_node = zTXt_node.getNextSibling();
1656 }
1657 } else if (name.equals("UnknownChunks")) {
1658 Node unknown_node = node.getFirstChild();
1659 while (unknown_node != null) {
1660 if (!unknown_node.getNodeName().equals("UnknownChunk")) {
1661 fatal(node,
1662 "Only an UnknownChunk may be a child of an UnknownChunks!");
1663 }
1664 String chunkType = getAttribute(unknown_node, "type");
1665 Object chunkData =
1666 ((IIOMetadataNode)unknown_node).getUserObject();
1667
1668 if (chunkType.length() != 4) {
1669 fatal(unknown_node,
1670 "Chunk type must be 4 characters!");
1671 }
1672 if (chunkData == null) {
1673 fatal(unknown_node,
1674 "No chunk data present in user object!");
1675 }
1676 if (!(chunkData instanceof byte[])) {
1677 fatal(unknown_node,
1678 "User object not a byte array!");
1679 }
1680 unknownChunkType.add(chunkType);
1681 unknownChunkData.add(((byte[])chunkData).clone());
1682
1683 unknown_node = unknown_node.getNextSibling();
1684 }
1685 } else {
1686 fatal(node, "Unknown child of root node!");
1687 }
1688
1689 node = node.getNextSibling();
1690 }
1691 }
1692
1693
1694
1695
1696
1697
1698
1699
1700
1701
1702 private boolean isValidKeyword(String s) {
1703 int len = s.length();
1704 if (len < 1 || len >= 80) {
1705 return false;
1706 }
1707 if (s.startsWith(" ") || s.endsWith(" ") || s.contains(" ")) {
1708 return false;
1709 }
1710 return isISOLatin(s, false);
1711 }
1712
1713
1714
1715
1716
1717
1718
1719
1720
1721
1722 private boolean isISOLatin(String s, boolean isLineFeedAllowed) {
1723 int len = s.length();
1724 for (int i = 0; i < len; i++) {
1725 char c = s.charAt(i);
1726 if (c < 32 || c > 255 || (c > 126 && c < 161)) {
1727
1728
1729 if (!isLineFeedAllowed || c != 0x10) {
1730 return false;
1731 }
1732 }
1733 }
1734 return true;
1735 }
1736
1737 private void mergeStandardTree(Node root)
1738 throws IIOInvalidTreeException {
1739 Node node = root;
1740 if (!node.getNodeName()
1741 .equals(IIOMetadataFormatImpl.standardMetadataFormatName)) {
1742 fatal(node, "Root must be " +
1743 IIOMetadataFormatImpl.standardMetadataFormatName);
1744 }
1745
1746 node = node.getFirstChild();
1747 while (node != null) {
1748 String name = node.getNodeName();
1749
1750 if (name.equals("Chroma")) {
1751 Node child = node.getFirstChild();
1752 while (child != null) {
1753 String childName = child.getNodeName();
1754 if (childName.equals("Gamma")) {
1755 float gamma = getFloatAttribute(child, "value");
1756 gAMA_present = true;
1757 gAMA_gamma = (int)(gamma*100000 + 0.5);
1758 } else if (childName.equals("Palette")) {
1759 byte[] red = new byte[256];
1760 byte[] green = new byte[256];
1761 byte[] blue = new byte[256];
1762 int maxindex = -1;
1763
1764 Node entry = child.getFirstChild();
1765 while (entry != null) {
1766 int index = getIntAttribute(entry, "index");
1767 if (index >= 0 && index <= 255) {
1768 red[index] =
1769 (byte)getIntAttribute(entry, "red");
1770 green[index] =
1771 (byte)getIntAttribute(entry, "green");
1772 blue[index] =
1773 (byte)getIntAttribute(entry, "blue");
1774 if (index > maxindex) {
1775 maxindex = index;
1776 }
1777 }
1778 entry = entry.getNextSibling();
1779 }
1780
1781 int numEntries = maxindex + 1;
1782 PLTE_red = new byte[numEntries];
1783 PLTE_green = new byte[numEntries];
1784 PLTE_blue = new byte[numEntries];
1785 System.arraycopy(red, 0, PLTE_red, 0, numEntries);
1786 System.arraycopy(green, 0, PLTE_green, 0, numEntries);
1787 System.arraycopy(blue, 0, PLTE_blue, 0, numEntries);
1788 PLTE_present = true;
1789 } else if (childName.equals("BackgroundIndex")) {
1790 bKGD_present = true;
1791 bKGD_colorType = PNGImageReader.PNG_COLOR_PALETTE;
1792 bKGD_index = getIntAttribute(child, "value");
1793 } else if (childName.equals("BackgroundColor")) {
1794 int red = getIntAttribute(child, "red");
1795 int green = getIntAttribute(child, "green");
1796 int blue = getIntAttribute(child, "blue");
1797 if (red == green && red == blue) {
1798 bKGD_colorType = PNGImageReader.PNG_COLOR_GRAY;
1799 bKGD_gray = red;
1800 } else {
1801 bKGD_red = red;
1802 bKGD_green = green;
1803 bKGD_blue = blue;
1804 }
1805 bKGD_present = true;
1806 }
1807
1808
1809
1810 child = child.getNextSibling();
1811 }
1812 } else if (name.equals("Compression")) {
1813 Node child = node.getFirstChild();
1814 while (child != null) {
1815 String childName = child.getNodeName();
1816 if (childName.equals("NumProgressiveScans")) {
1817
1818 int scans = getIntAttribute(child, "value");
1819 IHDR_interlaceMethod = (scans > 1) ? 1 : 0;
1820
1821
1822
1823 }
1824 child = child.getNextSibling();
1825 }
1826 } else if (name.equals("Data")) {
1827 Node child = node.getFirstChild();
1828 while (child != null) {
1829 String childName = child.getNodeName();
1830 if (childName.equals("BitsPerSample")) {
1831 String s = getAttribute(child, "value");
1832 StringTokenizer t = new StringTokenizer(s);
1833 int maxBits = -1;
1834 while (t.hasMoreTokens()) {
1835 int bits = Integer.parseInt(t.nextToken());
1836 if (bits > maxBits) {
1837 maxBits = bits;
1838 }
1839 }
1840 if (maxBits < 1) {
1841 maxBits = 1;
1842 }
1843 if (maxBits == 3) maxBits = 4;
1844 if (maxBits > 4 || maxBits < 8) {
1845 maxBits = 8;
1846 }
1847 if (maxBits > 8) {
1848 maxBits = 16;
1849 }
1850 IHDR_bitDepth = maxBits;
1851 } else if (childName.equals("SignificantBitsPerSample")) {
1852 String s = getAttribute(child, "value");
1853 StringTokenizer t = new StringTokenizer(s);
1854 int numTokens = t.countTokens();
1855 if (numTokens == 1) {
1856 sBIT_colorType = PNGImageReader.PNG_COLOR_GRAY;
1857 sBIT_grayBits = Integer.parseInt(t.nextToken());
1858 } else if (numTokens == 2) {
1859 sBIT_colorType =
1860 PNGImageReader.PNG_COLOR_GRAY_ALPHA;
1861 sBIT_grayBits = Integer.parseInt(t.nextToken());
1862 sBIT_alphaBits = Integer.parseInt(t.nextToken());
1863 } else if (numTokens == 3) {
1864 sBIT_colorType = PNGImageReader.PNG_COLOR_RGB;
1865 sBIT_redBits = Integer.parseInt(t.nextToken());
1866 sBIT_greenBits = Integer.parseInt(t.nextToken());
1867 sBIT_blueBits = Integer.parseInt(t.nextToken());
1868 } else if (numTokens == 4) {
1869 sBIT_colorType =
1870 PNGImageReader.PNG_COLOR_RGB_ALPHA;
1871 sBIT_redBits = Integer.parseInt(t.nextToken());
1872 sBIT_greenBits = Integer.parseInt(t.nextToken());
1873 sBIT_blueBits = Integer.parseInt(t.nextToken());
1874 sBIT_alphaBits = Integer.parseInt(t.nextToken());
1875 }
1876 if (numTokens >= 1 && numTokens <= 4) {
1877 sBIT_present = true;
1878 }
1879
1880
1881
1882 }
1883 child = child.getNextSibling();
1884 }
1885 } else if (name.equals("Dimension")) {
1886 boolean gotWidth = false;
1887 boolean gotHeight = false;
1888 boolean gotAspectRatio = false;
1889
1890 float width = -1.0F;
1891 float height = -1.0F;
1892 float aspectRatio = -1.0F;
1893
1894 Node child = node.getFirstChild();
1895 while (child != null) {
1896 String childName = child.getNodeName();
1897 if (childName.equals("PixelAspectRatio")) {
1898 aspectRatio = getFloatAttribute(child, "value");
1899 gotAspectRatio = true;
1900 } else if (childName.equals("HorizontalPixelSize")) {
1901 width = getFloatAttribute(child, "value");
1902 gotWidth = true;
1903 } else if (childName.equals("VerticalPixelSize")) {
1904 height = getFloatAttribute(child, "value");
1905 gotHeight = true;
1906
1907
1908
1909
1910
1911
1912
1913
1914
1915 }
1916 child = child.getNextSibling();
1917 }
1918
1919 if (gotWidth && gotHeight) {
1920 pHYs_present = true;
1921 pHYs_unitSpecifier = 1;
1922 pHYs_pixelsPerUnitXAxis = (int)(width*1000 + 0.5F);
1923 pHYs_pixelsPerUnitYAxis = (int)(height*1000 + 0.5F);
1924 } else if (gotAspectRatio) {
1925 pHYs_present = true;
1926 pHYs_unitSpecifier = 0;
1927
1928
1929 int denom = 1;
1930 for (; denom < 100; denom++) {
1931 int num = (int)(aspectRatio*denom);
1932 if (Math.abs(num/denom - aspectRatio) < 0.001) {
1933 break;
1934 }
1935 }
1936 pHYs_pixelsPerUnitXAxis = (int)(aspectRatio*denom);
1937 pHYs_pixelsPerUnitYAxis = denom;
1938 }
1939 } else if (name.equals("Document")) {
1940 Node child = node.getFirstChild();
1941 while (child != null) {
1942 String childName = child.getNodeName();
1943 if (childName.equals("ImageModificationTime")) {
1944 tIME_present = true;
1945 tIME_year = getIntAttribute(child, "year");
1946 tIME_month = getIntAttribute(child, "month");
1947 tIME_day = getIntAttribute(child, "day");
1948 tIME_hour =
1949 getIntAttribute(child, "hour", 0, false);
1950 tIME_minute =
1951 getIntAttribute(child, "minute", 0, false);
1952 tIME_second =
1953 getIntAttribute(child, "second", 0, false);
1954
1955
1956 }
1957 child = child.getNextSibling();
1958 }
1959 } else if (name.equals("Text")) {
1960 Node child = node.getFirstChild();
1961 while (child != null) {
1962 String childName = child.getNodeName();
1963 if (childName.equals("TextEntry")) {
1964 String keyword =
1965 getAttribute(child, "keyword", "", false);
1966 String value = getAttribute(child, "value");
1967 String language =
1968 getAttribute(child, "language", "", false);
1969 String compression =
1970 getAttribute(child, "compression", "none", false);
1971
1972 if (!isValidKeyword(keyword)) {
1973
1974 } else if (isISOLatin(value, true)) {
1975 if (compression.equals("zip")) {
1976
1977 zTXt_keyword.add(keyword);
1978 zTXt_text.add(value);
1979 zTXt_compressionMethod.add(Integer.valueOf(0));
1980 } else {
1981
1982 tEXt_keyword.add(keyword);
1983 tEXt_text.add(value);
1984 }
1985 } else {
1986
1987 iTXt_keyword.add(keyword);
1988 iTXt_compressionFlag.add(Boolean.valueOf(compression.equals("zip")));
1989 iTXt_compressionMethod.add(Integer.valueOf(0));
1990 iTXt_languageTag.add(language);
1991 iTXt_translatedKeyword.add(keyword);
1992 iTXt_text.add(value);
1993 }
1994 }
1995 child = child.getNextSibling();
1996 }
1997
1998
1999
2000
2001
2002
2003
2004
2005
2006
2007
2008
2009
2010
2011 }
2012
2013 node = node.getNextSibling();
2014 }
2015 }
2016
2017
2018 public void reset() {
2019 IHDR_present = false;
2020 PLTE_present = false;
2021 bKGD_present = false;
2022 cHRM_present = false;
2023 gAMA_present = false;
2024 hIST_present = false;
2025 iCCP_present = false;
2026 iTXt_keyword = new ArrayList<String>();
2027 iTXt_compressionFlag = new ArrayList<Boolean>();
2028 iTXt_compressionMethod = new ArrayList<Integer>();
2029 iTXt_languageTag = new ArrayList<String>();
2030 iTXt_translatedKeyword = new ArrayList<String>();
2031 iTXt_text = new ArrayList<String>();
2032 pHYs_present = false;
2033 sBIT_present = false;
2034 sPLT_present = false;
2035 sRGB_present = false;
2036 tEXt_keyword = new ArrayList<String>();
2037 tEXt_text = new ArrayList<String>();
2038 tIME_present = false;
2039 tRNS_present = false;
2040 zTXt_keyword = new ArrayList<String>();
2041 zTXt_compressionMethod = new ArrayList<Integer>();
2042 zTXt_text = new ArrayList<String>();
2043 unknownChunkType = new ArrayList<String>();
2044 unknownChunkData = new ArrayList<byte[]>();
2045 }
2046 }